package com.yanjue.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yanjue.client.BookClient;
import com.yanjue.common.entity.Book;
import com.yanjue.common.entity.UserLikeSee;
import com.yanjue.common.redisKey.RedisAccountKey;
import com.yanjue.common.redisKey.RedisExpire;
import com.yanjue.common.redisKey.RedisService;
import com.yanjue.common.util.Result;
import com.yanjue.common.util.ResultUtil;
import com.yanjue.common.vo.book.SimpleBookVO;
import com.yanjue.core.config.RedisCacheConfig;
import com.yanjue.mapper.UserLikeSeeMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yanjue.service.UserLikeSeeService;
import com.yanjue.service.task.LikeSeeClickTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;

/**
* @author yanju
* @description 针对表【user_like_see(用户喜欢)】的数据库操作Service实现
* @createDate 2025-01-16 12:33:44
*/
@Service
public class UserLikeSeeServiceImpl extends ServiceImpl<UserLikeSeeMapper, UserLikeSee>
    implements UserLikeSeeService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserLikeSeeServiceImpl.class);

    @Autowired
    private UserLikeSeeMapper likeSeeMapper;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ExecutorService commonQueueThreadPool;
    @Autowired
    private BookClient bookClient;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;




    /**
     * 喜欢点击处理
     * 将用户Id和喜欢的书一一对应，放在一张表里，存放的数据太多 数据庞大查询效率低
     * TODO 将书添加一个喜欢数字段，设计一个user_like_see表 使用redis 如果获取用户喜欢书单，
     * TODO 可以使用 分页或者懒加载提高性能.
     * TODO 用户点击时，立即操作Redis保证实时性。使用消息队列异步将操作日志持久化到数据库，确保最终一致性
     * @param userId 用户id
     * @param bookId 书id
     * @param value 是否是喜欢
     */
    @Override
    public Result likeSeeClick(Integer userId, String bookId, Integer value) {
        String key = RedisAccountKey.getUserLikeBooksKey(String.valueOf(userId));
        try {
            //0 取消喜欢 1增加喜欢 同时书的总喜欢数要变化，，用户的喜欢书单要变化
            if (0 == value){
                //this.likeSeeMapper.deleteById(bookId); sb bug
                //删除 delete from - where userID = ? and bookID = ?
                LambdaQueryWrapper<UserLikeSee> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(UserLikeSee::getUserId,userId)
                        .eq(UserLikeSee::getBookId,bookId);
                this.likeSeeMapper.delete(queryWrapper);
                //从redis里删除喜欢书籍
                stringRedisTemplate.opsForZSet().remove(key,bookId);

                //redisService.hashIncrement(key,bookId,-1);
            }else {
                //插入前检查是否已存在
                LambdaQueryWrapper<UserLikeSee> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(UserLikeSee::getUserId,userId).eq(UserLikeSee::getBookId,bookId);
                if (likeSeeMapper.selectCount(wrapper) == 0){
                    UserLikeSee likeSee = new UserLikeSee();
                    likeSee.setUserId(userId);
                    likeSee.setBookId(bookId);
                    this.likeSeeMapper.insert(likeSee);
                    //向redis里插入数据
                    stringRedisTemplate.opsForZSet().add(key,bookId, LocalDateTime.now().getSecond());
                    //redisService.hashIncrement(key,bookId,1);
                }

            }

            //数据库跟新完毕，更新缓存()异步x
            // TODO 异步 可以用Spring 的 @Async @EnableAsync 注解 可以定期同步redis和数据库的喜欢数
            LikeSeeClickTask task = new LikeSeeClickTask(redisService,bookId,value,likeSeeMapper);
            this.commonQueueThreadPool.execute(task);
        } catch (Exception e) {
            LOGGER.error("用户点击喜欢操作出现异常,e:{}",e);
            return ResultUtil.fail();
        }
        return ResultUtil.success();

    }





    /**
     * 获取图书喜欢数量
     *
     * @param bookId
     * @return
     */
    @Override
    public Result getBookLikesCount(String bookId) {
        //预缓存热门书籍喜欢数，结合汇总表高效查询
        //1.TODO 按理来讲，是需要远程调用 book 服务获取该书的总喜欢数，还有Long
        // 但是tmd表少个喜欢字段 直接从缓存里获取，缓存没有获取db
        String key = RedisAccountKey.ACCOUNT_CENTER_BOOK_LIKES_COUNT;
        //TODO 这里Long.class 其实对应的数据库字段也该是long，sb设计成integer
        Long likeCount = redisService.getHashVal(key, bookId, Long.class);
        if (likeCount == null){
            //2. 缓存没有 查db
            LambdaQueryWrapper<UserLikeSee> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserLikeSee::getBookId,bookId);

            Long dbCount = likeSeeMapper.selectCount(queryWrapper);
            likeCount = dbCount != null ? dbCount : 0L;
            //3.更新缓存
            redisService.setHashValExpire(key,bookId,likeCount, RedisExpire.MONTH);
        }
        return ResultUtil.success(likeCount);
    }





    /**
     * 获取用户喜欢书单
     * 分页查询 UserLikeSee 表后，循环远程调用获取书籍详情，性能差。
     * 缓存用户喜欢书籍ID列表 批量查询书籍详情
     * @param userId
     * @param size
     * @param limit
     * @return
     */
    @Override
    public Result getUserLikeBookList(Integer userId, Integer size, Integer limit) {
        try {
            String key = RedisAccountKey.getUserLikeBooksKey(String.valueOf(userId));
            //使用zrevrange分页获取bookId
            int start = (size - 1) * limit;
            int end = start + limit - 1;
            Set<String> bookIds = stringRedisTemplate.opsForZSet().range(key, start, end);

            if (bookIds != null){
                List<SimpleBookVO> books = new ArrayList<>();
                for (String bookId : bookIds) {
                    SimpleBookVO vo = new SimpleBookVO();
                    Book book = this.bookClient.getBookById(bookId).getData();
                    if (book != null){
                        BeanUtils.copyProperties(book,vo);
                        books.add(vo);
                    }
                }

                return ResultUtil.success(books);
            }
            //查db
            Page<UserLikeSee> pageRequest = new Page<>(size,limit);
            LambdaQueryWrapper<UserLikeSee> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserLikeSee::getUserId,userId);
            //查询分页结果
            Page<UserLikeSee> pageResult = likeSeeMapper.selectPage(pageRequest,queryWrapper);
            List<UserLikeSee> records = pageResult.getRecords();
            List<SimpleBookVO> books = new ArrayList<>();
            for (UserLikeSee likeSee : records) {
                SimpleBookVO vo = new SimpleBookVO();
                Book book = this.bookClient.getBookById(likeSee.getBookId()).getData();
                if (book != null){
                    BeanUtils.copyProperties(book,vo);
                    books.add(vo);
                }
            }

            return ResultUtil.success(books);

            /*// 批量查询书籍详情
            List<SimpleBookVO> books = bookClient.batchGetBooks(new ArrayList<>(bookIds)).stream()
                    .map(book -> {
                        SimpleBookVO vo = new SimpleBookVO();
                        BeanUtils.copyProperties(book, vo);
                        return vo;
                    })
                    .collect(Collectors.toList());*/
            //转化结果

        } catch (BeansException e) {
            LOGGER.error("获取用户喜欢书单异常e:",e);
            return ResultUtil.fail();
        }
    }


}




